
#include "StdAfx.h"
#include "HidCtrl.h"


#define	RECV_BUF_SIZE	64
#define	RECV_BUF_NUM	16
static DWORD WINAPI recvThreadProc(LPVOID lpParameter)
{
	CHidCtrl *pThis = (CHidCtrl *)lpParameter;
	PBYTE pRecvBuf = NULL, pCurBuf;
	int nBufIdx = 0;

	pRecvBuf = new BYTE [RECV_BUF_SIZE * RECV_BUF_NUM];
	while (1)
	{
		pCurBuf = pRecvBuf + (nBufIdx * RECV_BUF_SIZE);
		if (FALSE == pThis->RecvThreadProc(pCurBuf, RECV_BUF_SIZE))
			break;
		nBufIdx++;
		if (RECV_BUF_NUM <= nBufIdx)
			nBufIdx = 0;
	}
	if (pRecvBuf)
		delete [] pRecvBuf;

	return 0;
}


CHidCtrl::CHidCtrl(void)
{
	m_hWndNotify = NULL;
	m_hDeviceNotify = NULL;

	m_hDeviceSend = INVALID_HANDLE_VALUE;
	m_hDeviceRecv = INVALID_HANDLE_VALUE;
	m_hEventCompletion = NULL;
	memset(&m_OverlapRecv, 0, sizeof(m_OverlapRecv));
	m_hThreadRecv = NULL;
}

CHidCtrl::~CHidCtrl(void)
{
	CloseDevice();
	UnregDevNotify();
}


BOOL CHidCtrl::RegDevNotify(HWND hWndNotify)
{
	GUID guidHid = {0,};
	HidD_GetHidGuid(&guidHid);

	DEV_BROADCAST_DEVICEINTERFACE filter = {0,};
	filter.dbcc_size = sizeof(filter);
	filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
	filter.dbcc_classguid = guidHid;
	m_hDeviceNotify = RegisterDeviceNotification(hWndNotify, (PVOID)&filter, DEVICE_NOTIFY_WINDOW_HANDLE);

	if (m_hDeviceNotify)
		m_hWndNotify = hWndNotify;

	return (m_hDeviceNotify ? TRUE : FALSE);
}

void CHidCtrl::UnregDevNotify(void)
{
	if (m_hDeviceNotify)
	{
		UnregisterDeviceNotification(m_hDeviceNotify);
		m_hDeviceNotify = NULL;
	}
}


BOOL CHidCtrl::IsOpen(void)
{
	return (INVALID_HANDLE_VALUE != m_hDeviceSend) ? TRUE : FALSE;
}

BOOL CHidCtrl::OpenDevice(WORD wVid, WORD wPid, BYTE bIfn, LPSTR lpString, int nCount)
{
	CStringArray aryDevicePath;

	GUID guidHid = {0,};
	HidD_GetHidGuid(&guidHid);

	HDEVINFO hDevInfo = SetupDiGetClassDevs(&guidHid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
	if (INVALID_HANDLE_VALUE != hDevInfo)
	{
		char szHidDeviceFilter[32] = {0,};	// low-case : "vid_ffff" or "vid_ffff&pid_1200"
		sprintf_s(szHidDeviceFilter, _countof(szHidDeviceFilter), "vid_%04x&pid_%04x&mi_%02d", wVid, wPid, bIfn);

		SP_INTERFACE_DEVICE_DATA ifData = {sizeof(SP_INTERFACE_DEVICE_DATA),};
		for (DWORD dwIndex=0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &guidHid, dwIndex, &ifData); ++dwIndex)
		{
			DWORD dwRequiredSize = 0;
			SP_DEVINFO_DATA did = {sizeof(SP_DEVINFO_DATA),};

			SetupDiGetDeviceInterfaceDetail(hDevInfo, &ifData, NULL, 0, &dwRequiredSize, NULL);
			PSP_INTERFACE_DEVICE_DETAIL_DATA pDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA)new BYTE [dwRequiredSize];
			pDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
			if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &ifData, pDetail, dwRequiredSize, NULL, &did))
			{
				// Add the link to the list of all HID devices
				if (strstr(pDetail->DevicePath, szHidDeviceFilter) != NULL)
				{
					aryDevicePath.Add(pDetail->DevicePath);
				}
			}
			delete [] pDetail;
		}

		SetupDiDestroyDeviceInfoList(hDevInfo);
	}

	for (int i=0; i<aryDevicePath.GetCount(); i++)
	{
		CloseDevice();

		OutputDebugString(aryDevicePath.GetAt(i));
		OutputDebugString(_T("\r\n"));

		m_hDeviceSend = CreateFile(aryDevicePath.GetAt(i), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
		m_hDeviceRecv = CreateFile(aryDevicePath.GetAt(i), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
		//DWORD dwError = GetLastError();
		m_hEventCompletion = CreateEvent(NULL, FALSE, FALSE, NULL);
		m_hThreadRecv = CreateThread(NULL, 0, recvThreadProc, this, 0, 0);
		if ((INVALID_HANDLE_VALUE != m_hDeviceSend) && (INVALID_HANDLE_VALUE != m_hDeviceRecv)
			&& (NULL != m_hEventCompletion) && (NULL != m_hThreadRecv))
		{
			m_OverlapRecv.hEvent = m_hEventCompletion;

			if (lpString && (0 < nCount))
			{
				memset(lpString, 0, nCount);
				strcpy_s(lpString, nCount, aryDevicePath.GetAt(i));
			}

			return TRUE;
		}
	}

	return FALSE;
}

void CHidCtrl::CloseDevice(void)
{
	if (INVALID_HANDLE_VALUE != m_hDeviceSend)
	{
		CloseHandle(m_hDeviceSend);
		m_hDeviceSend = INVALID_HANDLE_VALUE;
	}

	if (INVALID_HANDLE_VALUE != m_hDeviceRecv)
	{
		CloseHandle(m_hDeviceRecv);
		m_hDeviceRecv = INVALID_HANDLE_VALUE;
	}

	if (m_hEventCompletion)
	{
		CloseHandle(m_hEventCompletion);
		m_hEventCompletion = NULL;
	}

	if (m_hThreadRecv)
	{
		WaitForSingleObject(m_hThreadRecv, INFINITE);
		CloseHandle(m_hThreadRecv);
		m_hThreadRecv = NULL;
	}
}

BOOL CHidCtrl::RecvThreadProc(PBYTE pBuf, DWORD dwCount)
{
	if (INVALID_HANDLE_VALUE == m_hDeviceRecv)
		return FALSE;

	DWORD bRet, dwBytesRead;

	// +++ ReadOverlapped +++
	bRet = ReadFile(m_hDeviceRecv, pBuf, dwCount, &dwBytesRead, &m_OverlapRecv);
	if (bRet)
		SetEvent(m_hEventCompletion);
	else
		bRet = (ERROR_IO_PENDING == GetLastError());
	if (FALSE == bRet)
		return bRet;
	while (INVALID_HANDLE_VALUE != m_hDeviceRecv)
	{
		if (WAIT_OBJECT_0 == WaitForSingleObject(m_hEventCompletion, 1000))
			break;
	}
	// --- ReadOverlapped ---

	::PostMessage(m_hWndNotify, UM_HID_PACKET, (WPARAM)dwCount, (LPARAM)pBuf);

	return TRUE;
}


DWORD CHidCtrl::SendReport(PBYTE pBuf, DWORD dwCount)
{
	DWORD dwWitten = 0;

	if (INVALID_HANDLE_VALUE != m_hDeviceSend)
		WriteFile(m_hDeviceSend, pBuf, dwCount, &dwWitten, 0);

	return dwWitten;
}
